﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics.Contracts;

namespace Netium.Compiler.BackEnd.Interpreter.RuntimeStack
{
    class RtStack : IStack
    {
        Stack<IStackFrame> _stack = new Stack<IStackFrame>();

        public void Push(IStackFrame stackFrame)
        {
            Contract.Requires<ArgumentNullException> (stackFrame != null);

            _stack.Push(stackFrame);

            OnStackPushed(stackFrame);
        }

        IStackFrame PushNew()
        {
            var stackFrame = new RtStackFrame();

            _stack.Push(stackFrame);

            OnStackPushed(stackFrame);

            return stackFrame;
        }

        public IStackFrame Pop()
        {
            var frame = _stack.Pop();
            OnStackPoped(frame);
            return frame;
        }

        public IStackFrame Peek()
        {
            return _stack.Peek();
        }

        public int Count
        {
            get { return _stack.Count; }
        }

        public IStackFrame this[int index]
        {
            get { return _stack.ElementAt(index); }
        }

        public IList<IStackFrame> Frames
        {
            get { return _stack.ToArray(); }
        }

        EventHandler<StackEventArgs> StackPushed;
        EventHandler<StackEventArgs> StackPoped;

        private void OnStackPushed(IStackFrame frame)
        {
            if (StackPushed != null)
            {
                var e = new StackEventArgs(frame);
                StackPushed(this, e);
            }
        }

        private void OnStackPoped(IStackFrame frame)
        {
            if (StackPoped != null)
            {
                var e = new StackEventArgs(frame);
                StackPoped(this, e);
            }
        }
    }

    class RtStackFrame : IStackFrame
    {
        Dictionary<string, IMemorySlot> _slots = new Dictionary<string, IMemorySlot>();

        public int Count
        {
            get { return _slots.Keys.Count; }
        }

        public IMemorySlot this[string name]
        {
            get { return _slots.ContainsKey(name) ? _slots[name] : null;  }
        }

        public IMemorySlot AllocateSlot(string name)
        {
            Contract.Requires<DuplicatedEntryException>(!_slots.ContainsKey(name));

            var slot = new RtMemorySlot(name);

            _slots.Add(name, slot);

            return slot;
        }
    }

    class RtMemorySlot : IMemorySlot
    {
        public RtMemorySlot(string name)
        {
            Contract.Requires<ArgumentNullException>(!string.IsNullOrWhiteSpace(name));

            Name = name;
        }

        public string Name
        {
            get;
            private set;
        }

        public ISymbol Symbol
        {
            get;
            set;
        }

        public object Value
        {
            get;
            set;
        }
    }
}
